#!/usr/bin/env ruby

require 'open3'
require 'find'
require 'optparse'

dirty_only = false
directories = [Dir.pwd]
@ignores = []

def is_git_env(path, deep = false)
  exist = File.exists?(File.join(path, '.git'))
  return true if exist
  return false unless deep

  return false if path == '/'
  return is_git_env(File.dirname(path), true)
end

def is_clean(status_info)
  return !status_info[/"git [^"]+"/]
end

def print_status_info(path, status_info)
  puts "#{path}:"
  status_info.split("\n").each do |line|
    puts "    #{line}"
  end
  puts
end

def ignored?(path)
  @ignores.any? do |ignored_path|
    return true if path.start_with?(ignored_path)
  end

  false
end

begin
  OptionParser.new do |opts|
    opts.banner = <<~BANNER
      递归显示路径下所有项目的 Git 环境的状态。用法：

              gits deep-status [options] -- [paths]

      选项：

    BANNER

    opts.on('-d', '--dirty-only', '只显示有未提交的或未推送状态的仓库') do
      dirty_only = true
    end

    opts.on('-i', '--ignore PATH', '忽略指定目录下的所有 Git 仓库，可重复指定') do |path|
      @ignores << path
    end

    opts.separator ""
    opts.separator <<~EOF
      示例：

          # 当前路径，只列出 dirty 的仓库
          gits deep-status -d

          # 列出 p1 和 p2 下的仓库，同时忽略 p1/d p2/d
          gits deep-status -i p1/d -i p2/d -- p1 p2
    EOF
  end.parse!
rescue OptionParser::ParseError => ex
  STDERR.puts ex.message
  exit 1
end
directories = ARGV unless ARGV.empty?

directories.each do |directory|
  if is_git_env(directory, true)
    stdout = Dir.chdir(directory) { `git status` }
    print_status_info(directory, stdout) unless dirty_only && is_clean(stdout)
  else
    Find.find(directory) do |path|
      next if ignored?(path)
      if FileTest.directory?(path) && is_git_env(path)
        stdout = Dir.chdir(path) { `git status` }
        print_status_info(path, stdout) unless dirty_only && is_clean(stdout)
        Find.prune
      end
    end
  end
end
